package cn.ekuma.epos.catalog;

import java.awt.Cursor;
import java.awt.Image;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.util.Iterator;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import prefuse.Constants;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.action.ActionList;
import prefuse.action.RepaintAction;
import prefuse.action.assignment.ColorAction;
import prefuse.action.assignment.SizeAction;
import prefuse.action.layout.RandomLayout;
import prefuse.action.layout.graph.ForceDirectedLayout;
import prefuse.activity.Activity;
import prefuse.activity.ActivityAdapter;
import prefuse.activity.ActivityListener;
import prefuse.controls.ControlAdapter;
import prefuse.data.Schema;
import prefuse.data.Table;
import prefuse.data.tuple.TupleSet;
import prefuse.render.DefaultRendererFactory;
import prefuse.render.LabelRenderer;
import prefuse.util.ColorLib;
import prefuse.util.PrefuseLib;
import prefuse.util.force.DragForce;
import prefuse.util.force.ForceItem;
import prefuse.util.force.ForceSimulator;
import prefuse.util.force.NBodyForce;
import prefuse.util.force.SpringForce;
import prefuse.visual.VisualItem;
import prefuse.visual.sort.ItemSorter;

import com.openbravo.bean.Product;
import com.openbravo.data.loader.ImageUtils;
import com.openbravo.pos.bean.ProductExt;
import com.openbravo.pos.panels.event.ProductChangedListener;
import java.awt.BorderLayout;

public class JProductPrefusePanel extends JPanel {

	CatelogManager manager;
	private static final String ANCHORITEM = "_anchorItem";
    private static final Schema ANCHORITEM_SCHEMA = new Schema();
    static {
        ANCHORITEM_SCHEMA.addColumn(ANCHORITEM, ForceItem.class);
    }
	
	/**
	 * Create the panel.
	 */
	public JProductPrefusePanel(CatelogManager manager) {
         this.manager=manager;
		initComponents();
	}
	private void initComponents() {
		setLayout(new BorderLayout(0, 0));
	}
	
	
	public void reflush(){
		this.removeAll();
		this.add(new DataMountain(getTableForProductList(manager.getProducts())),BorderLayout.CENTER);
	}
	
	public void  addProductChangedListener(ProductChangedListener l){
		listenerList.add(ProductChangedListener.class, l);
	}
	
	public void removeProductChangedListener(ProductChangedListener l){
		listenerList.remove(ProductChangedListener.class, l);
	}
	
	public void fireProductChanged(Product obj){
		for(ProductChangedListener l:listenerList.getListeners(ProductChangedListener.class))
			l.baseBeanChanged(obj, obj);	 
	}

	private Table getTableForProductList(List<ProductExt> objs){
		Table data=new Table();
		data.addColumn("image","CONCAT('/images/',id,'.01.MZZZZZZZ.jpg')");
		return data;
	}
	
	public class DataMountain extends Display {
	    public DataMountain(Table t) {
	        super(new Visualization());
	        m_vis.addTable("data", t); 
	        LabelRenderer nodeRenderer = new LabelRenderer(null, "image"){
				@Override
				protected Image getImage(VisualItem item) {
					int rowIndex=item.getRow();
					return ImageUtils.readImage(manager.getProducts().get(rowIndex).getImage());
				}
	        };
	        nodeRenderer.setTextField(null);
	        nodeRenderer.setVerticalAlignment(Constants.BOTTOM);
	        nodeRenderer.setHorizontalPadding(0);
	        nodeRenderer.setVerticalPadding(0);
	        nodeRenderer.setMaxImageDimensions(400,300);
	        
	        m_vis.setRendererFactory(new DefaultRendererFactory(nodeRenderer));
	        
	        ActionList init = new ActionList();
	        init.add(new RandomLayout());
	        init.add(new DataMountainSizeAction());
	        m_vis.putAction("init", init);
	        
	        ActionList update = new ActionList();
	        update.add(new DataMountainSizeAction());
	        update.add(new ColorAction("data", VisualItem.STROKECOLOR) {
	            public int getColor(VisualItem item) {
	                return ColorLib.rgb((item.isHover() ? 255 : 0), 0, 0);
	            }
	        });
	        update.add(new RepaintAction());
	        m_vis.putAction("update", update);

	        // we run this to make sure the forces are stabilized
	        ActionList preforce = new ActionList(100);
	        preforce.add(new DataMountainForceLayout(true));
	        m_vis.putAction("preforce", preforce);

	        // this will cause docs to move out of the way when dragging
	        final ForceDirectedLayout fl = new DataMountainForceLayout(false);
	        ActivityListener fReset = new ActivityAdapter() {
	            public void activityCancelled(Activity a) {
	                fl.reset(); 
	             } 
	        };
	        ActionList forces = new ActionList(Activity.INFINITY);
	        forces.add(fl);
	        forces.add(update);
	        forces.addActivityListener(fReset);
	        m_vis.putAction("forces", forces);
	        
	        setSize(1024,768);
	        setDamageRedraw(false); // disable due to Java2D image clipping errors
	        setBorder(BorderFactory.createEmptyBorder(30,20,5,20));
	        setItemSorter(new DataMountainSorter());
	        addControlListener(new DataMountainControl());
	        
	        // pre-load images, otherwise they will be loaded asynchronously
	        nodeRenderer.getImageFactory().preloadImages(m_vis.items(),"image");
	        
	        // initialize and present the interface
	        m_vis.run("init");
	        m_vis.runAfter("preforce", "update");
	        m_vis.run("preforce");
	    }
	    
	    // ------------------------------------------------------------------------
	    
	    public  class DataMountainSorter extends ItemSorter {
	        public int score(VisualItem item) {
	            return (int)(1000*item.getY());
	        }
	    } // end of inner class DataMountainComparator
	    
	    public class DataMountainSizeAction extends SizeAction {
	        public double getSize(VisualItem item) {
	            double y = item.getEndY();
	            return 0.2 + y/1000;
	        }
	    } // end of inner class DataMountainSizeAction
	    
	    
	    public class DataMountainForceLayout extends ForceDirectedLayout {
	        
	        public DataMountainForceLayout(boolean enforceBounds) {
	            super("data",enforceBounds,false);
	            
	            ForceSimulator fsim = new ForceSimulator();
	            fsim.addForce(new NBodyForce(-0.4f, 25f, NBodyForce.DEFAULT_THETA));
	            fsim.addForce(new SpringForce(1e-5f,0f));
	            fsim.addForce(new DragForce());
	            setForceSimulator(fsim);
	            
	            m_nodeGroup = "data";
	            m_edgeGroup = null;
	        }
	        
	        protected float getMassValue(VisualItem n) {
	            return n.isHover() ? 5f : 1f;
	        }

	        public void reset() {
	            Iterator iter = m_vis.visibleItems(m_nodeGroup);
	            while ( iter.hasNext() ) {
	                VisualItem item = (VisualItem)iter.next();
	                ForceItem aitem = (ForceItem)item.get(ANCHORITEM);
	                if ( aitem != null ) {
	                    aitem.location[0] = (float)item.getEndX();
	                    aitem.location[1] = (float)item.getEndY();
	                }
	            }
	            super.reset();
	        }
	        protected void initSimulator(ForceSimulator fsim) {
	            // make sure we have force items to work with
	            TupleSet t = (TupleSet)m_vis.getGroup(m_group);
	            t.addColumns(ANCHORITEM_SCHEMA);
	            t.addColumns(FORCEITEM_SCHEMA);
	            
	            Iterator iter = m_vis.visibleItems(m_nodeGroup);
	            while ( iter.hasNext() ) {
	                VisualItem item = (VisualItem)iter.next();
	                // get force item
	                ForceItem fitem = (ForceItem)item.get(FORCEITEM);
	                if ( fitem == null ) {
	                    fitem = new ForceItem();
	                    item.set(FORCEITEM, fitem);
	                }
	                fitem.location[0] = (float)item.getEndX();
	                fitem.location[1] = (float)item.getEndY();
	                fitem.mass = getMassValue(item);
	                
	                // get spring anchor
	                ForceItem aitem = (ForceItem)item.get(ANCHORITEM);
	                if ( aitem == null ) {
	                    aitem = new ForceItem();
	                    item.set(ANCHORITEM, aitem);
	                    aitem.location[0] = fitem.location[0];
	                    aitem.location[1] = fitem.location[1];
	                }
	                
	                fsim.addItem(fitem);
	                fsim.addSpring(fitem, aitem, 0);
	            }     
	        }       
	    } // end of inner class DataMountainForceLayout
	    
	    public class DataMountainControl extends ControlAdapter {
	        public static final String URL = "http://www.amazon.com/exec/obidos/tg/detail/-/";
	        private VisualItem activeItem;
	        private Point2D down = new Point2D.Double();
	        private Point2D tmp = new Point2D.Double();
	        private boolean wasFixed, dragged;
	        private boolean repaint = false;
	        
	        public void itemEntered(VisualItem item, MouseEvent e) {
	            Display d = (Display)e.getSource();
	            d.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
	            d.setToolTipText(item.getString("id"));
	            activeItem = item;
	            wasFixed = item.isFixed();
	        }
	        
	        public void itemExited(VisualItem item, MouseEvent e) {
	            if ( activeItem == item ) {
	                activeItem = null;
	                item.setFixed(wasFixed);
	            }
	            Display d = (Display)e.getSource();
	            d.setToolTipText(null);
	            d.setCursor(Cursor.getDefaultCursor());
	        }
	        
	        public void itemPressed(VisualItem item, MouseEvent e) {
	            if (!SwingUtilities.isLeftMouseButton(e)) return;
	            
	            // set the focus to the current node
	            Visualization vis = item.getVisualization();
	            vis.getFocusGroup(Visualization.FOCUS_ITEMS).setTuple(item);
	            
	            item.setFixed(true);
	            dragged = false;
	            Display d = (Display)e.getComponent();
	            down = d.getAbsoluteCoordinate(e.getPoint(), down);
	            
	            vis.run("forces");
	        }
	        
	        public void itemReleased(VisualItem item, MouseEvent e) {
	            if (!SwingUtilities.isLeftMouseButton(e)) return;
	            if ( dragged ) {
	                activeItem = null;
	                item.setFixed(wasFixed);
	                dragged = false;
	            }
	            // clear the focus
	            Visualization vis = item.getVisualization();
	            vis.getFocusGroup(Visualization.FOCUS_ITEMS).clear();

	            vis.cancel("forces");
	        }
	        
	        public void itemClicked(VisualItem item, MouseEvent e) {
	            if (!SwingUtilities.isLeftMouseButton(e)) return;
	            if ( e.getClickCount() == 2 ) {
	                String id = item.getString("id");
	                
	            }
	        }
	        
	        public void itemDragged(VisualItem item, MouseEvent e) {
	            if (!SwingUtilities.isLeftMouseButton(e)) return;
	            dragged = true;
	            Display d = (Display)e.getComponent();
	            tmp = d.getAbsoluteCoordinate(e.getPoint(), tmp);
	            double dx = tmp.getX()-down.getX();
	            double dy = tmp.getY()-down.getY();
	            
	            PrefuseLib.setX(item, null, item.getX()+dx);
	            PrefuseLib.setY(item, null, item.getY()+dy);
	            down.setLocation(tmp);
	            if ( repaint )
	                item.getVisualization().repaint();
	        }
	    } // end of inner class DataMountainControl
	    
	} // end of class DataMountain

}